﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Vibz.Common;
using System.Diagnostics;
using System.IO.Pipes;
using System.IO;
using System.Threading;
using System.Collections.Concurrent;
using Vibz.Common.Model;

namespace Vibz.Server.Controller.UI
{
    public partial class FormServer : Form
    {
        XmlModelSource xModelSource;
        string xmlSource = "ModelConfig.xml";
        Dictionary<string, ModelInfo> processList = new Dictionary<string, ModelInfo>();
        delegate void UpdateControllCallback<T>(T control, object arg);

        public FormServer()
        {
            InitializeComponent();

            DataGridViewImageColumn AcceptColumn = new DataGridViewImageColumn();
            AcceptColumn.Icon = Vibz.Server.Controller.UI.Properties.Resources.accept;
            AcceptColumn.ImageLayout = DataGridViewImageCellLayout.Normal;
            AcceptColumn.ValuesAreIcons = true;
            AcceptColumn.Name = "Accept";
            AcceptColumn.CellTemplate.Style.BackColor = Color.Honeydew;
            dgvModelView.Columns.Add(AcceptColumn);

            DataGridViewImageColumn StartColumn = new DataGridViewImageColumn();
            StartColumn.Icon = Vibz.Server.Controller.UI.Properties.Resources.run;
            StartColumn.ImageLayout = DataGridViewImageCellLayout.NotSet;
            StartColumn.ValuesAreIcons = true;
            StartColumn.Name = "Start";
            StartColumn.CellTemplate.Style.BackColor = Color.Honeydew;
            dgvModelView.Columns.Add(StartColumn);

            DataGridViewImageColumn StopColumn = new DataGridViewImageColumn();
            StopColumn.Icon = Vibz.Server.Controller.UI.Properties.Resources.stop;
            StopColumn.ImageLayout = DataGridViewImageCellLayout.Normal;
            StopColumn.ValuesAreIcons = true;
            StopColumn.Name = "Stop";
            StopColumn.CellTemplate.Style.BackColor = Color.Honeydew;
            dgvModelView.Columns.Add(StopColumn);

            DataGridViewImageColumn SaveColumn = new DataGridViewImageColumn();
            SaveColumn.Icon = Vibz.Server.Controller.UI.Properties.Resources.save;
            SaveColumn.ImageLayout = DataGridViewImageCellLayout.Normal;
            SaveColumn.ValuesAreIcons = true;
            SaveColumn.Name = "Save";
            SaveColumn.CellTemplate.Style.BackColor = Color.Honeydew;
            dgvModelView.Columns.Add(SaveColumn);

            LoadModels();
        }

        void LoadModels()
        {
            try
            {
                dgvModelView.Rows.Clear();
                xModelSource = new XmlModelSource(xmlSource);
                dgvModelView.DataSource = xModelSource.Models.Select(mv => new ModelView(mv.Name)).ToList();
            }
            catch (Exception exc)
            {
                lstMessage.Items.Add("Could not load the configuration file." + exc.Message);
            }
        }

        private void UpdateControlData<T>(T control, object arg)
        {
            if ((control as Control).InvokeRequired)
            {
                UpdateControllCallback<T> d = new UpdateControllCallback<T>(UpdateControlData);
                this.Invoke(d, new object[] { control, arg });
            }
            else
            {
                string method = ((object[])arg).GetValue(0).ToString();
                switch (method)
                {
                    case "AddListBox":
                        string message = (string)((object[])arg).GetValue(1);
                        (control as ListBox).Items.Add(message);
                        break;
                    case "SetServerConnectionPath":
                        string mName = (string)((object[])arg).GetValue(1);
                        string cPath = (string)((object[])arg).GetValue(2);
                        
                        foreach(DataGridViewRow row in (control as DataGridView).Rows)
                        {
                            if (row.Cells["ModelName"].Value.ToString().ToLower() == mName.ToLower())
                            {
                                row.Cells["ModelName"].ToolTipText = "Clients of this model service can connect at '" + cPath + "'. Right click on this row to copy the connection path.";
                                lstMessage.Items.Add(row.Cells["ModelName"].ToolTipText);
                                row.ContextMenuStrip = new ContextMenuStrip();
                                row.ContextMenuStrip.Items.Add("Copy Connection Path");
                                row.ContextMenuStrip.Items[0].Tag = cPath;
                                row.ContextMenuStrip.Items[0].Image = Vibz.Server.Controller.UI.Properties.Resources.copy;
                                row.ContextMenuStrip.Items[0].Click += new EventHandler(FormServer_Click);
                            }
                        }
                        break;
                }
            }
        }

        void FormServer_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(((ToolStripItem)sender).Tag.ToString());
        }

        private void SetMessagePipe(object args)
        {
            string mName = ((object[])args)[0].ToString();
            ConcurrentQueue<string> queue = (ConcurrentQueue<string>)((object[])args)[1];
            try
            {
                var pipe = new NamedPipeServerStream(mName, PipeDirection.InOut, 1,
                                        PipeTransmissionMode.Byte, PipeOptions.Asynchronous,
                                        8000, 8000);
                StreamWriter sw = new StreamWriter(pipe);
                UpdateControlData<ListBox>(lstMessage, new object[] { "AddListBox", "Messaging service started." });
                IAsyncResult result = pipe.BeginWaitForConnection(asyncResult =>
                {
                    using (var conn = (NamedPipeServerStream)asyncResult.AsyncState)
                    {
                        try
                        {
                            UpdateControlData<ListBox>(lstMessage, new object[] { "AddListBox", "Model service '" + mName + "' service connected to messaging service." });
                            conn.EndWaitForConnection(asyncResult);

                            using (StreamReader sr = new StreamReader(pipe))
                            {
                                string connPath = sr.ReadLine();
                                UpdateControlData<DataGridView>(dgvModelView, new object[] { "SetServerConnectionPath", mName, connPath });
                                while (true)
                                {
                                    try
                                    {
                                        if (!pipe.IsConnected)
                                            continue;

                                        sw.AutoFlush = true;
                                        string command = String.Empty;
                                        if (queue.TryDequeue(out command) && !string.IsNullOrEmpty(command))
                                        {
                                            SendMessage(sw, pipe, command);
                                        }
                                    }
                                    catch (Exception exc)
                                    {
                                        UpdateControlData<ListBox>(lstMessage, new object[] { "AddListBox", 
                                            "Error occured. " + exc.Message + "\r\n Listener for model service '" + mName + "' is closing." });
                                        break;
                                    }
                                }
                            }
                        }
                        catch (Exception exc)
                        {
                            UpdateControlData<ListBox>(lstMessage, new object[] { "AddListBox", exc.Message });
                        }
                        conn.WaitForPipeDrain();
                    }
                }, pipe);
                
            }
            catch (Exception exc)
            {
                UpdateControlData<ListBox>(lstMessage, new object[] { "AddListBox", "Error occured while Listening to model service '" + mName + "'." + exc.Message });
            }
        }

        void SetMode(RunningStatus rMode, string mName)
        {
            switch (rMode)
            {
                case RunningStatus.Accept:
                    ModelInformation mi = xModelSource.GetModelInformation(mName);
                    string cmdexePath = @"Vibz.Server.exe";
                    string cmdArguments = String.Format("{0} {1} {2} /K", "xml", xmlSource, mName);
                    ProcessStartInfo psi = new ProcessStartInfo(cmdexePath, cmdArguments);
                    Process p = new Process();
                    p.StartInfo = psi;
                    if (p.Start())
                    {
                        ConcurrentQueue<string> queue = new ConcurrentQueue<string>();

                        Thread listenerThread = new Thread(new ParameterizedThreadStart(SetMessagePipe));
                        listenerThread.Start(new object[] { mName, queue });

                        processList.Add(mName, new ModelInfo(p, listenerThread, queue));
                        queue.Enqueue("Be In acceptance mode.");
                    }
                    UpdateControlData<ListBox>(lstMessage, new object[] { "AddListBox", "Model service '" + mName + "' is now accepting connections." });
                    break;
                case RunningStatus.Run:
                    processList[mName].Queue.Enqueue("start");
                    UpdateControlData<ListBox>(lstMessage, new object[] { "AddListBox", "Model service '" + mName + "' is now running." });
                    break;
                case RunningStatus.Stop:
                    if (processList[mName].Process.HasExited)
                        break;
                    processList[mName].Queue.Enqueue("stop");
                    processList[mName].Process.Kill();
                    processList.Remove(mName);
                    UpdateControlData<ListBox>(lstMessage, new object[] { "AddListBox", "Model service '" + mName + "' has been stopped." });
                    break;
            }
        }

        public void SendMessage(StreamWriter sw, NamedPipeServerStream pipe, string message)
        {
            if (sw != null)
            {
                sw.WriteLine(message);
                sw.Flush();
                //pipe.WaitForPipeDrain();
            }
        }

        private void dgvModelView_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if (dgvModelView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value == null)
                return;
            string command = dgvModelView.Columns[e.ColumnIndex].Name.ToString();
            string mName = dgvModelView.Rows[e.RowIndex].Cells["ModelName"].Value.ToString();
            switch (command.ToLower())
            {
                case "accept":
                    SetMode(RunningStatus.Accept, mName);
                    dgvModelView.Rows[e.RowIndex].Cells["RunningStatus"].Value = RunningStatus.Accept;
                    break;
                case "start":
                    SetMode(RunningStatus.Run, mName);
                    dgvModelView.Rows[e.RowIndex].Cells["RunningStatus"].Value = RunningStatus.Run;
                    break;
                case "stop":
                    SetMode(RunningStatus.Stop, mName);
                    dgvModelView.Rows[e.RowIndex].Cells["RunningStatus"].Value = RunningStatus.Stop;
                    break;
                case "save":
                    processList[mName].Queue.Enqueue("save");
                    break;
            }
        }
    }
}
